package com.ruyuan.process.engine.annoations;

import com.ruyuan.process.engine.annoations.EnableProcessEngine;
import com.ruyuan.process.engine.config.ClassPathXmlProcessParser;
import com.ruyuan.process.engine.model.ProcessContextFactory;
import com.ruyuan.process.engine.model.ProcessModel;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.support.BeanDefinitionBuilder;
import org.springframework.beans.factory.support.BeanDefinitionRegistry;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.ImportBeanDefinitionRegistrar;
import org.springframework.core.type.AnnotationMetadata;

import java.util.ArrayList;
import java.util.List;

/**
 * @author zhonghuashishan
 * @version 1.0
 */
@Slf4j
public class ProcessNodeRegistrar implements ImportBeanDefinitionRegistrar {

    @Override
    public void registerBeanDefinitions(AnnotationMetadata annotationMetadata, BeanDefinitionRegistry beanDefinitionRegistry) {
        try {
            // AnnotationMetadata，这个是描述你当前的注解的元数据
            // @EnableProcessEngine注解，通过注解名称，就可以通过getAnnotationAttributes拿到你这个注解的属性集合
            // 拿到你的这个注解的属性集合之后，就可以拿到你的注解value的值，注解里定义的核心值
            // 就是我们的流程定义文件名称
            String configFile = (String) annotationMetadata.getAnnotationAttributes(
                    EnableProcessEngine.class.getName()).get("value");

            // 1. 解析得到流程
            // 通过ClassPathXmlProcessParser，基于class path类路径的xml处理解析组件，对我们的xml配置文件进行解析
            // 就可以拿到一堆的ProcessModel
            // ProcessModel，每个ProcessModel其实代表了你的一个流程定义的配置数据，一个流程定义，就对应了一个ProcessModel
            // 解析我们的流程定义的文件，去拿到我们的所有流程定义对应的ProcessModel配置数据
            ClassPathXmlProcessParser classPathXmlProcessParser = new ClassPathXmlProcessParser(configFile);
            List<ProcessModel> processLists = classPathXmlProcessParser.parse();

            // 2. 注册ProcessContextFactory
            // BeanDefinitionBuilder，是spring提供的接口，构造一个bean，基于ProcessContextFactory自定义类，来定义bean
            // 就可以给他加入进去，bean实例化的时候，构造函数的参数，解析出来的process定义数据
            // 基于xml解析出来的流程定义数据模型，会去封装成ProcessContextFactory，是我们框架提供出来的最核心的API
            // 这个东西持有了我们所有定义的流程数据，我们可以基于他去驱动各种流程的推进和执行，他自动去执行补偿

            // 在这里我们可以学习到，如何去正规化通过代码编程，构建spring beandefinition
            // 需要去使用spring体用的BeanDefinitionBuilder，使用构造器模式，来构建出来对应的一个bean
            // 指定的class构建出来一个Builder，构建那个class的bean，构建class的bean，必然会通过反射，去构建class的实例对象
            // 如果说你的class他的构造函数是默认构造函数，是空入参构造函数，还好说，直接基于反射构造就可以了
            // 如果说你的bean实例的构造函数，是需要传入一些参数的，此时应该怎么办呢？
            // 编码构建spring bean定义的时候，需要指定我们的构造函数各个入参都是什么值，分析一下
            BeanDefinitionBuilder bdb = BeanDefinitionBuilder.rootBeanDefinition(ProcessContextFactory.class);
            bdb.addConstructorArgValue(new ArrayList<>(processLists));
            bdb.addConstructorArgReference("springBeanInstanceCreator"); // 依赖和引用spring容器里的其他bean，springBeanInstanceCreator
            // 基于spring的bean定义注册中心，把我们自己构造的一个bean定义给他注册进去就可以了
            // 后续对这个类class构造实例对象的时候，就会基于我们指定构造函数去进行构造，参数传入进去就可以了
            beanDefinitionRegistry.registerBeanDefinition(ProcessContextFactory.class.getName(), bdb.getBeanDefinition());
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}
